import datetime
import logging
import traceback

from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.domain.AlipayTradePayModel import AlipayTradePayModel
from alipay.aop.api.domain.GoodsDetail import GoodsDetail
from alipay.aop.api.domain.SettleDetailInfo import SettleDetailInfo
from alipay.aop.api.domain.SettleInfo import SettleInfo
from alipay.aop.api.domain.SubMerchant import SubMerchant
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
from alipay.aop.api.request.AlipayTradePayRequest import AlipayTradePayRequest
from alipay.aop.api.response.AlipayTradePayResponse import AlipayTradePayResponse
from alipay.aop.api.util.SignatureUtils import verify_with_rsa
from flask import request
from flask_restful import Resource, abort

from main.apis.api_constant import APPID, APP_PRIVATE_KEY, ALIPAY_PUBLIC_KEY, HTTP_OK
from main.apis.user.utils import ordinary_login_required, get_user_id, get_now_time
from main.apis.utils import generate_pay_token
from main.ext import cache
from main.models.goods.goods_model import Goods
from main.models.user.user_model import OrderModel, OrderGoodsModel, PAID, NOT_VALID
from main.settings import SERVER_HOST


class PayResource(Resource):
    @ordinary_login_required
    def get(self, order_id: int):
        user_id = get_user_id()
        order = OrderModel.query.filter_by(id=order_id, o_user_id=user_id).first()
        if not order:
            abort(404, error='无此订单')
        is_paid = order.o_status
        is_valid = order.o_valid
        if (not is_paid and not is_valid) or order.o_time < get_now_time()[0] - datetime.timedelta(minutes=21):
            abort(403, error='无效订单')
        elif is_paid and not is_valid:
            abort(403, error='此订单已支付')
        elif is_paid and is_valid:  # 后端除非人为修改过订单表，否则不会报此错
            abort(500, error='服务器原因导致订单异常，请联系客服处理')

        # logging.basicConfig(
        #     level=logging.INFO,
        #     format='%(asctime)s %(levelname)s %(message)s',
        #     filemode='a'
        # )
        # logger = logging.getLogger('')

        alipay_client_config = AlipayClientConfig()
        alipay_client_config.server_url = 'https://openapi.alipaydev.com/gateway.do'
        alipay_client_config.app_id = APPID
        alipay_client_config.app_private_key = APP_PRIVATE_KEY
        alipay_client_config.alipay_public_key = ALIPAY_PUBLIC_KEY

        # client = DefaultAlipayClient(alipay_client_config, logger)
        client = DefaultAlipayClient(alipay_client_config)

        # model.body = 'iPhone 11 Pro Max'
        # model.goods_detail = goods_list
        # model.operator_id = 'test_001'
        # model.out_trade_no = '2222222222'
        # model.product_code = 'FACE_TO_FACE_PAYMENT'
        # model.scene = 'bar_code'
        # model.store_id = ''
        # model.subject = '测试alipay'
        # model.timeout_express = '90m'
        # model.total_amount = 1
        # request = AlipayTradePayRequest(model)

        now_raw = datetime.datetime.now()
        now = datetime.datetime.strftime(now_raw, '%Y%m%d%H%M%S%f')  # %f 微秒

        model = AlipayTradePagePayModel()
        model.out_trade_no = now  # 支付宝订单号
        model.total_amount = order.o_price  # 订单总价
        model.subject = "支付宝测试"
        model.body = "多商品订单-支付宝测试"
        model.product_code = "FAST_INSTANT_TRADE_PAY"  # 销售产品码 https://opensupport.alipay.com/support/knowledge/20582/201602373525?ant_source=zsearch
        model.timeout_express = '20m'  # 支付宝订单不支付的超时时间
        order_goods = []
        for order_good in OrderGoodsModel.query.filter_by(o_order_id=order_id).all():
            good = GoodsDetail()
            good.goods_name = order_good.goods.g_name
            good.price = order_good.goods.g_price
            good.quantity = order_good.o_goods_count
            order_goods.append(good)
        model.goods_detail = order_goods
        # settle_detail_info = SettleDetailInfo()  # 结算详细信息
        # settle_detail_info.amount = 50
        # settle_detail_info.trans_in_type = "userId"
        # settle_detail_info.trans_in = "2088302300165604"
        # settle_detail_infos = list()
        # settle_detail_infos.append(settle_detail_info)
        # settle_info = SettleInfo()
        # settle_info.settle_detail_infos = settle_detail_infos
        # model.settle_info = settle_info
        # sub_merchant = SubMerchant()  # 支付宝商户版注册支付宝账号id
        # sub_merchant.merchant_id = "2088301300153242"
        # model.sub_merchant = sub_merchant
        request = AlipayTradePagePayRequest(biz_model=model)
        # request.return_url = 'http://127.0.0.1:5000/pay/?orderid=' + str(order.id)

        token = generate_pay_token()
        cache.set(token, order_id, timeout=60 * 30)  # 缓存有效期应大于支付宝支付有效期
        # 支付成功后跳转的页面
        request.return_url = SERVER_HOST + 'paid/?order_token=' + token
        # notify_url： 支付宝服务器主动通知商户服务器里指定的页面http或https路径，网站上线后可用支付宝通知结果改变订单支付状态
        # 详见 https://opensupport.alipay.com/support/knowledge/20582/201602051354?ant_source=zsearch
        # request.notify_url = ''

        # 得到构造的请求，如果http_method是GET，则是一个带完成请求参数的url，如果http_method是POST，则是一段HTML表单片段
        response = client.page_execute(request, http_method="GET")  # GET方法会生成一个支付链接
        # response = client.page_execute(request, http_method="POST")  # POST方法会生成一个form表单用于动态生成支付宝支付的html页面

        data = {
            'status': HTTP_OK,
            'msg': 'ok',
            'data': {
                'url': response,
                'order_id': now
            }
        }
        return data


class PaidResource(Resource):
    # @ordinary_login_required
    def get(self):

        # start  对return_url传递的参数验签
        args = request.args.to_dict()  # request.args转换为dict才能修改
        sign = args.pop('sign', None)
        order_token = args.pop('order_token', None)
        if not order_token:
            abort(403, error='订单异常')
        args.pop('sign_type')
        params = sorted(args.items(), key=lambda e: e[0], reverse=False)  # 取出字典元素按key的字母升序排序形成列表
        message = '&'.join(u"{}={}".format(k, v) for k, v in params).encode()  # 将列表转为二进制参数字符串
        # print(message)
        try:
            status = verify_with_rsa(ALIPAY_PUBLIC_KEY.encode('utf-8').decode('utf-8'), message, sign)
        except:
            status = False
        # end  对return_url传递的参数验签

        order_id = cache.get(order_token)
        cache.delete(order_token)  # 取到order_id后不管支付是否成功都直接删除相关缓存
        if not order_id:
            abort(403, error='订单失效或已支付')
        order = OrderModel.query.get(order_id)
        if not status:
            order.o_valid = NOT_VALID
            order.save()
            abort(403, error='支付失败')

        # 支付成功后，订单支付状态(o_status)改为已支付，有效性(o_valid)改为无效(无效订单不可再支付)
        order.o_status = PAID
        order.o_valid = NOT_VALID
        order.save()

        data = {
            'status': HTTP_OK,
            'msg': '支付成功',
            'order_id': order_id
        }
        return data
